package filesystem

import (
	"crypto/md5"
	"errors"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"sort"
	"strings"
)

// 处理相同文件的信息
func processDuplicatedFileInfo(path, suffix string, result map[string][]string) (fail bool, size int64, err2 error) {
	fail = false
	if suffix != "" && !strings.HasSuffix(strings.ToUpper(path), suffix) {
		return true, 0, errors.New(fmt.Sprintf("没有后缀suffix:%s", suffix))
	}
	var (
		file *os.File
	)

	if file, err2 = os.Open(path); err2 != nil {
		return true, 0, err2
	}
	defer file.Close()

	hash := md5.New()
	if _, err2 = io.Copy(hash, file); err2 != nil {
		return true, 0, err2
	}

	fileHash := hash.Sum(nil)
	fileHashString := string(fileHash)

	if _, ok := result[fileHashString]; !ok {
		result[fileHashString] = []string{}
	}
	if result[fileHashString] != nil && len(result[fileHashString]) > 0 {
		//获取文件大小
		var (
			fileInfo os.FileInfo
		)
		if fileInfo, err2 = file.Stat(); err2 == nil {
			size = fileInfo.Size()
		}
	}
	result[fileHashString] = append(result[fileHashString], path)

	return
}

// FindDuplicateFilesWithSameContent 文件名称前缀相同 内容相同finds all the duplicate files in a directory where the file names can be different but the contents must be the same.
func FindDuplicateFilesWithSameContent(dirPth string, files string, suffix string, ignoreFile string) (size int64,
	result map[string][]string, err error) {
	result = make(map[string][]string)
	if suffix != "" {
		suffix = strings.ToUpper(suffix)
	}
	dirPaths := strings.Split(dirPth, ",")
	for _, dirPath := range dirPaths {
		dirPath = strings.TrimSpace(dirPath)
		if dirPath != "" {
			if ignoreFile != "" {
				//判断是否需要忽略
				if ignore, _ := ShouldIgnored2(dirPath, ignoreFile); ignore {
					continue
				}
			}
			err = filepath.Walk(dirPath, func(path string, info os.FileInfo, err error) (err2 error) {
				if err != nil {
					err2 = err
					return
				}

				if !info.Mode().IsRegular() {
					return
				}
				var (
					fileSize int64
					ignore   bool
					err3     error
				)
				if ignoreFile != "" {
					//判断是否需要忽略
					if ignore, _ = ShouldIgnored2(path, ignoreFile); ignore {
						return
					}
				}
				if _, fileSize, err3 = processDuplicatedFileInfo(path, suffix, result); err3 == nil {
					size += fileSize
				} else if !strings.Contains(err3.Error(), "没有后缀suffix") {
					err2 = err3
				}

				return
			})
		} else {
			fileList := strings.Split(files, ",")
			for _, fileName := range fileList {
				var (
					fileSize int64
				)
				if _, fileSize, err = processDuplicatedFileInfo(fileName, suffix, result); err == nil {
					size += fileSize
				}
			}
		}
	}

	if err != nil {
		if strings.Contains(err.Error(), "没有后缀") {
			err = nil
		} else {
			return
		}
	}
	fileNameHash := make(map[string]string, 0)
	for fileHash, filePaths := range result {
		if len(filePaths) < 2 {
			delete(result, fileHash)
		} else {
			sort.Slice(filePaths, func(i, j int) bool {
				if len(filePaths[i]) == len(filePaths[j]) {
					return filePaths[i] < filePaths[j]
				} else {
					return len(filePaths[i]) < len(filePaths[j])
				}
			})

			//fileName := filepath.Base(filePaths[0])

			result[fileHash] = filePaths
			fileNameHash[fileHash] = filePaths[0]
		}

	}
	for fileHash, fileName := range fileNameHash {
		result[fileName] = result[fileHash]
		delete(result, fileHash)
	}

	return
}

// FindDuplicateFilesWithSameName 相同的文件名称
func FindDuplicateFilesWithSameName(dirPth string, suffix string) (result map[string][]string, err error) {
	result = make(map[string][]string)
	if suffix != "" {
		suffix = strings.ToUpper(suffix)
	}
	err = filepath.Walk(dirPth, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		if !info.Mode().IsRegular() {
			return nil
		}
		if suffix != "" && !strings.HasSuffix(strings.ToUpper(path), suffix) {
			return nil
		}

		fileName := filepath.Base(path)
		absPath, err := filepath.Abs(path)
		if err != nil {
			return err
		}

		if _, ok := result[fileName]; !ok {
			result[fileName] = []string{}
		}

		result[fileName] = append(result[fileName], absPath)

		return nil
	})

	if err != nil {
		return nil, err
	}

	for fileName, filePaths := range result {
		if len(filePaths) < 2 {
			delete(result, fileName)
		} else {
			sort.Slice(filePaths, func(i, j int) bool {
				if len(filePaths[i]) == len(filePaths[j]) {
					return filePaths[i] < filePaths[j]
				} else {
					return len(filePaths[i]) < len(filePaths[j])
				}
			})
			result[filePaths[0]] = filePaths
			if filePaths[0] != fileName {
				delete(result, fileName)
			}
		}
	}

	return result, nil
}

func DeleteAllDuplicateContentFiles(dirPth string, files string, suffix string, ignoreFile string) (size int64,
	deletedFilesMap map[string][]string, err error) {
	var (
		duplicatedFilesMap map[string][]string
	)
	if size, duplicatedFilesMap, err = FindDuplicateFilesWithSameContent(dirPth, files, suffix, ignoreFile); err == nil &&
		duplicatedFilesMap != nil && len(duplicatedFilesMap) > 0 {
		deletedFilesMap = make(map[string][]string, 0)
		for fileName, paths := range duplicatedFilesMap {
			for index, path := range paths {
				if index > 0 {
					os.Remove(path)
				}
			}
			deletedFilesMap[fileName] = paths[1:]
		}
	}

	return
}
